package com.spa.application.service.common;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Header;
import com.spa.application.command.Login;
import com.spa.application.command.admin.UserInfoAddCommand;
import com.spa.application.dto.login.LoginDTO;
import com.spa.application.dto.login.LoginUser;
import com.spa.application.query.admin.AdminLoginQuery;
import com.spa.domain.entity.AuthUser;
import com.spa.domain.entity.SysUserEntity;
import com.spa.domain.entity.UserInfo;
import com.spa.domain.service.AuthUserService;
import com.spa.domain.service.SysAdminUserService;
import com.spa.domain.service.UserInfoService;
import com.spa.domain.service.UserTicketService;
import com.spa.infrastructure.enums.dict.base.LoginWayEnum;
import com.spa.infrastructure.enums.dict.base.RequestSourceEnum;
import com.spa.infrastructure.enums.dict.base.SourceTypeEnum;
import com.spa.infrastructure.enums.dict.coupon.CouponTypeEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.mapper.AuthUserMapperStruct;
import com.spa.infrastructure.repository.po.AuthUserPO;
import com.spa.infrastructure.service.douyin.DouYinMiNiService;
import com.spa.infrastructure.service.douyin.result.JsCode2SessionResult;
import com.spa.infrastructure.service.sms.GenerateSmsCodeKey;
import com.spa.infrastructure.service.wx.Constant;
import com.spa.infrastructure.service.wx.WXJsSdkService;
import com.spa.infrastructure.service.wx.WXLoginService;
import com.spa.infrastructure.service.wx.msg.chat.WxMpErrorMsgEnum;
import com.spa.infrastructure.service.wx.result.AccessTokenResult;
import com.spa.infrastructure.service.wx.result.ChatUserInfoResult;
import com.spa.infrastructure.util.*;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Slf4j
@Service
public class LoginApplicationService {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private UserInfoService userInfoService;

    @Resource
    private UserTicketService userTicketService;

    @Resource
    private SysAdminUserService sysAdminUserService;

    @Resource
    private AuthUserService authUserService;

    @Autowired
    private WXJsSdkService wxJsSdkService;

    @Resource
    private WXLoginService wxLoginService;

    @Resource
    private GenerateSmsCodeKey smsCodeKey;

    @Autowired
    private DouYinMiNiService douYinService;

    /**
     * 用户端 授权短信 注册/登录
     *
     * @return token
     */
    public LoginDTO memberAuthSmsRegisterLogin(Login login, SourceTypeEnum sourceTypeEnum) {
        //校验短信
        validateCaptchaSms(login, RequestSourceEnum.Member);
        //获取授权
        String openId = login.getAuthId();
        if (StringUtils.isEmpty(openId)) {
            openId = getOpenId(sourceTypeEnum, login.getCode());
            login.setAuthId(openId);
        }

        String token = authRegisterLogin(login, RequestSourceEnum.Member, sourceTypeEnum);
        return new LoginDTO(token, openId);
    }

    /**
     * 用户端 授权登录
     *
     * @return token
     */
    public LoginDTO memberAuthLogin(Login login, SourceTypeEnum sourceTypeEnum) {
        //获取授权
        String openId = login.getAuthId();
        if (StringUtils.isEmpty(openId)) {
            openId = getOpenId(sourceTypeEnum, login.getCode());
            login.setAuthId(openId);
        }

        String token = getAuthLoginToken(login, RequestSourceEnum.Member, sourceTypeEnum);
        return new LoginDTO(token, openId);
    }

    /**
     * 平台端 账号密码
     *
     * @return token
     */
    public LoginDTO adminLogin(AdminLoginQuery query, LoginWayEnum loginWay) {
        LoginDTO loginDTO = new LoginDTO();
        Login login = new Login();
        login.setUsername(query.getUsername());
        login.setPassword(query.getPassword());
        LoginUser loginUser = passwordLogin(login, RequestSourceEnum.Admin);
        if (BeanUtil.isEmpty(loginUser)) {
            return null;
        }
        loginDTO.setToken(getLoginToken(RequestSourceEnum.Admin, loginUser));
        loginDTO.setUsername(loginUser.getSysUser().getNickName());
        return loginDTO;
    }

    /**
     * 添加更新授权用户
     *
     * @param userId
     * @param openId
     * @param sourceTypeEnum
     * @param requestSourceEnum
     * @return
     */
    public void addAuthInfo(Long userId, String openId, SourceTypeEnum sourceTypeEnum, RequestSourceEnum requestSourceEnum) {
        AuthUser authUser = authUserService.info(userId, sourceTypeEnum.getCode(), requestSourceEnum.getCode());
        if (BeanUtil.isNotEmpty(authUser)) {
            authUser.setUserId(userId);
            authUser.setOpenId(openId);
            authUser.setUserSource(requestSourceEnum.getCode());
            authUserService.updateInfoById(AuthUserMapperStruct.INSTANCE.entity2po(authUser));
        } else {
            //添加授权信息
            AuthUserPO po = new AuthUserPO();
            po.setOpenId(openId);
            po.setSource(sourceTypeEnum.getCode());
            po.setUserSource(requestSourceEnum.getCode());
            po.setUserId(userId);
            authUserService.addInfo(po);
        }
    }

    /**
     * 用户端 手机号 注册用户
     *
     * @return 结果
     */
    public UserInfo phoneRegister(String phone) {
        UserInfoAddCommand command = new UserInfoAddCommand();
        command.setPhone(phone);
        UserInfo userInfo = userInfoService.registerUserInfo(command);
        //用户端注册后置方法
        registerBefore(userInfo.getId());
        return userInfo;
    }

    /**
     * 注册后置
     *
     * @return 结果
     */
    @Async
    public void registerBefore(Long userId) {
        //添加注册优惠券
        userTicketService.addUserTicket(CouponTypeEnum.$10.getCode(), userId);
    }

    /**
     * 获取登录 生成token
     *
     * @return token
     */
    public String getLoginToken(RequestSourceEnum rs, LoginUser lu) {
        try {
            StpUtil.logout(lu.getUserId());
            UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader(Header.USER_AGENT.getValue()));
            //区分手机与pc 同端互斥
            StpUtil.login(lu.getUserId(), rs.getCode());
            LoginUser loginUser = new LoginUser(lu, userAgent, rs);
            if (BeanUtil.isNotEmpty(lu.getShopUser())) {
                loginUser.setShopId(lu.getShopUser().getShopId());
            } else if (BeanUtil.isNotEmpty(lu.getMassagist())) {
                loginUser.setShopId(lu.getMassagist().getShopId());
            }
            SessionUtil.setUser(loginUser);
        } catch (Exception e) {
            Assert.isTrue(false, "生成token出错，" + e.getMessage());
        }
        // 生成token
        return StpUtil.getTokenValue();
    }

    /**
     * 认证 注册/登录
     *
     * @param sourceTypeEnum 授权来源类型
     * @return token
     */
    public String authRegisterLogin(Login login, RequestSourceEnum requestSourceEnum, SourceTypeEnum sourceTypeEnum) {
        LoginUser loginUser = null;
        //获取授权信息
        AuthUser authUser = authUserService.infoByOpenIdAndSource(login.getAuthId(), sourceTypeEnum, requestSourceEnum);
        if (RequestSourceEnum.Member == requestSourceEnum) {
            loginUser = getMemberLoginUser(authUser, login, requestSourceEnum, sourceTypeEnum);
        }
        if (BeanUtil.isEmpty(loginUser)) return "";
        return getLoginToken(requestSourceEnum, loginUser);
    }

    /**
     * 获取认证 登录
     *
     * @param sourceTypeEnum 授权来源类型
     * @return token
     */
    public String getAuthLoginToken(Login login, RequestSourceEnum requestSourceEnum, SourceTypeEnum sourceTypeEnum) {
        LoginUser loginUser = null;
        //获取授权信息
        AuthUser authUser = authUserService.infoByOpenIdAndSource(login.getAuthId(), sourceTypeEnum, requestSourceEnum);
        if (BeanUtil.isEmpty(authUser)) {
            authUser = new AuthUser();
            authUser.setOpenId(login.getAuthId());
        }

        if (RequestSourceEnum.Member.equals(requestSourceEnum)) {
            loginUser = getMemberLoginUser(authUser);
        }
        if (BeanUtil.isEmpty(loginUser)) return "";
        return getLoginToken(requestSourceEnum, loginUser);
    }

    /**
     * 手机短信校验
     *
     * @param login Login
     * @return 结果
     */
    public void validateCaptchaSms(Login login, RequestSourceEnum requestSource) {
        Assert.isTrue(StringUtils.isNotEmpty(login.getPhone()), "请确认手机号是否正常输入");
        Assert.isTrue(StringUtils.isNotEmpty(login.getSmsCode()), "请确验证码是否正常输入");
        String verifyKey = smsCodeKey.generateSmsCodeKey(login.getPhone(), requestSource.getCode());
        String smsCaptcha = redisUtil.get(verifyKey);
        if (smsCaptcha == null) {
            throw new SpaException("短信验证码错误");
        }
        if (!login.getSmsCode().equalsIgnoreCase(smsCaptcha)) {
            throw new SpaException("验证码错误");
        }
    }

    /**
     * 获取授权openId
     *
     * @return 结果
     */
    public String getOpenId(SourceTypeEnum sourceTypeEnum, String code) {
        String openId = "";
        if (SourceTypeEnum.WeChat.equals(sourceTypeEnum)) {
            AccessTokenResult accessTokenResult = wxLoginService.getAccessToken(code);
            openId = accessTokenResult.getOpenid();
        } else if (SourceTypeEnum.DouYinMiNi.equals(sourceTypeEnum)) {
            //sourceType = SourceTypeEnum.DouYinMiNi;
            JsCode2SessionResult jsCode2SessionResult = douYinService.login(code, "");
            openId = jsCode2SessionResult.getOpenid();
        } else {
            throw new SpaException("请确认来源是否正确，该来源无法获取授权来源不匹配");
        }
        Assert.isTrue(StringUtils.isNotEmpty(openId), "获取授权ID失败");
        return openId;
    }

    /**
     * 登录前置校验  账号密码
     *
     * @param login
     */
    public void validateCheck(Login login) {
        String username = login.getUsername();
        String password = login.getPassword();
        Assert.isTrue(StringUtils.isNotEmpty(username) || StringUtils.isNotEmpty(password), "用户名或密码不能为空");
        // 密码如果不在指定范围内 错误
        if (login.getPassword().length() < Constant.PASSWORD_MIN_LENGTH
                || password.length() > Constant.PASSWORD_MAX_LENGTH) {
            throw new SpaException("用户不存在/密码错误");
        }
    }

    public Integer getWxConcerned(Long userId, RequestSourceEnum requestSourceEnum) {
        log.info("getWxConcerned : {}", userId);
        AuthUser au = authUserService.info(userId, SourceTypeEnum.WeChat.getCode(), requestSourceEnum.getCode());
        log.info("AuthUser : {}", au);
        if (BeanUtil.isEmpty(au)) return 0;
        ChatUserInfoResult result = wxJsSdkService.getUserInfo(SourceTypeEnum.WeChat, au.getOpenId());
        if (ObjectUtil.isNotNull(result.getErrcode()) && result.getErrcode() != 0) {
            log.error("查询是否关注公众号 error msg={}", WxMpErrorMsgEnum.findMsgByCode(result.getErrcode()));
            throw new SpaException("查询是否关注公众号失败");
        }
        return result.getSubscribe();
    }

    /**
     * & 用户端 获取登录用户
     *
     * @author: lig
     * @date: 2024/1/12
     */
    public LoginUser getMemberLoginUser(AuthUser authUser, Login login, RequestSourceEnum requestSourceEnum, SourceTypeEnum sourceTypeEnum) {
        LoginUser loginUser = new LoginUser();
        UserInfo userInfo = null;
        if (BeanUtil.isNotEmpty(authUser)) {
            if (StringUtils.isNotEmpty(login.getPhone())) {
                userInfo = userInfoService.userInfoByPhoneOne(login.getPhone());
            } else {
                userInfo = userInfoService.userInfoById(authUser.getUserId());
            }
        }
        if (BeanUtil.isEmpty(userInfo) && StringUtils.isNotEmpty(login.getPhone())) {
            //注册用户
            userInfo = phoneRegister(login.getPhone());
        }
        if (BeanUtil.isNotEmpty(userInfo)) {
            //添加更新授权
            addAuthInfo(userInfo.getId(), login.getAuthId(), sourceTypeEnum, requestSourceEnum);
            userInfo.setOpenId(login.getAuthId());
            loginUser.setUserId(userInfo.getId());
            loginUser.setUserInfo(userInfo);
            return loginUser;
        }
        return loginUser;
    }

    /**
     * & 用户端 获取登录用户
     *
     * @author: lig
     * @date: 2024/1/12
     */
    public LoginUser getMemberLoginUser(AuthUser authUser) {
        LoginUser loginUser = new LoginUser();
        UserInfo userInfo = userInfoService.userInfoById(authUser.getUserId());
        if (BeanUtil.isNotEmpty(userInfo)) {
            userInfo.setOpenId(authUser.getOpenId());
            loginUser.setUserId(userInfo.getId());
            loginUser.setUserInfo(userInfo);
        }
        return loginUser;
    }

    /**
     * & 密码登录
     *
     * @author: lig
     * @date: 2024/1/12
     */
    public LoginUser passwordLogin(Login login, RequestSourceEnum requestSourceEnum) {
        LoginUser loginUser = new LoginUser();
        validateCheck(login);
        if (RequestSourceEnum.Admin == requestSourceEnum) {
            SysUserEntity adminUser = sysAdminUserService.infoOneByUsername(login.getUsername());
            Assert.isTrue(BeanUtil.isNotEmpty(adminUser), "用户不存在");
            Assert.isTrue(PasswdUtils.matchesPassword(login.getPassword(), adminUser.getPassword()), "密码错误");
            loginUser.setUserId(adminUser.getUserId());
            loginUser.setSysUser(adminUser);
        }
        return loginUser;
    }

}
